#ifndef __MY_DATA__
#define __MY_DATA__
#include <unordered_map>
#include <pthread.h>
#include "util.hpp"
#include "config.hpp"

namespace cloud
{
    // 服务器需要的数据信息结构体
    struct BackupInfo
    {
        bool pack_flag;        // 是否被压缩的标志
        size_t fsize;          // 文件大小
        time_t atime;          // 最后一次访问时间
        time_t mtime;          // 最后一次修改时间
        std::string real_path; // 文件的实际存储路径
        std::string pack_path; // 压缩包存储路径名称
        std::string url;       // 请求的资源路径

        // 获取文件各项信息，将文件信息填充到BackupInfo中
        bool NewBackupInfo(const std::string realpath)
        {
            // 实例化一个工具类对象
            cloud::FileUtil fu(realpath);
            if (fu.Exists() == false)
            {
                std::cout << "new backupinfo : file not exists!";
                return false;
            }
            // 压缩标志位为false
            pack_flag = false;
            // 调用函数完成文件大小计算及访问和修改时间
            fsize = fu.FileSize();
            mtime = fu.LastMTime();
            atime = fu.LastATime();
            // 文件的实际存储路径等于我们传入的路径
            real_path = realpath;
            // 获取单例类的操作句柄
            cloud::Config *config = cloud::Config::GetInstance();
            // 获取压缩文件目录和压缩包后缀名
            std::string packdir = config->GetPackDir();
            std::string packsuffix = config->GetPackfileSuffix();

            // edg: ./backdir/a.txt  -->   ./packdir/a.txt.lz
            //  压缩包相对完整路径 -压缩文件目录+文件名+压缩包后缀
            pack_path = packdir + fu.FileName() + packsuffix;

            // 下载前缀+纯文件名称
            // edg: ./backdir/a.txt  -->   /download/a.txt
            std::string download_prefix = config->GetDownloadPrefix();
            url = download_prefix + fu.FileName();
            return true;
        }
    };

    class DataManager
    {
    private:
        // 数据信息访问是通过哈希表来进行存储的，而持久化存储是通过文件来进行存储的
        std::string _backup_file; // 持久化存储的文件
        // 这是一个哈希表，首先是通过string作为key值,以BackupInfo作为val值存储在内存中的
        std::unordered_map<std::string, BackupInfo> _table;
        // 使用读写锁-读共享，写互斥-头文件<pthread.h>
        pthread_rwlock_t _rwlock;

    public:
        DataManager()
        {
            // 持久化存储文件从配置文件中读取，直接通过配置单例类来获取
            //  backup_file是实际的持久化存储文件，在配置信息里面所有的，通过配置信息类来获取
            _backup_file = Config::GetInstance()->GetBackUpFile();
            // 然后就是锁的初始化,调用函数
            pthread_rwlock_init(&_rwlock, NULL);
            InitLoad(); // 初始化胡
        }
        // 析构函数中要对读写锁进行释放
        ~DataManager()
        {
            pthread_rwlock_destroy(&_rwlock);
        }

        // 新增数据，新增的是BackupInfo，以url作为key存入哈希表中就可以了
        bool Insert(const BackupInfo &info)
        {
            // 每次新增的时候就要加锁
            pthread_rwlock_wrlock(&_rwlock);
            // url和info形成键值对
            _table[info.url] = info;
            // 然后解锁
            pthread_rwlock_unlock(&_rwlock);
            this->Storage();
            return true;
        }
        // 修改数据-压缩之后标志位改变
        bool Update(const BackupInfo &info)
        {
            // 修改和新增本质差不多
            pthread_rwlock_wrlock(&_rwlock);
            _table[info.url] = info; // 因为当key值相同时会覆盖掉之前的值
            pthread_rwlock_unlock(&_rwlock);
            this->Storage();
            return true;
        }
        // 查询接口，当我们要下载时获取的是单个文件的信息，如果是要展示的界面获取的是文件所有的信息
        bool GetOneByUrl(const std::string &url, BackupInfo *info)
        {
            // 通过url来查询数据,因为要操作table，所以进行一个加锁操作
            pthread_rwlock_wrlock(&_rwlock);
            // 因为url是table表中的key值可以通过find来查找
            auto it = _table.find(url);
            pthread_rwlock_unlock(&_rwlock);
            if (it == _table.end())
            {
                // 没有找到
                return false;
            }
            *info = it->second;
            return true;
        }
        // 根据文件实际存储路径获取文件信息，获取不到说明文件没有插入成功，重新插入
        bool GetOneByRealPath(const std::string &realpath, BackupInfo *info)
        {
            // 这里不能根据key值去找，所以只能遍历去找
            // 先加锁
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.begin();
            for (; it != _table.end(); it++)
            {
                if (it->second.real_path == realpath)
                {
                    *info = it->second;
                    // 解锁
                    pthread_rwlock_unlock(&_rwlock);
                    return true;
                }
            }
            pthread_rwlock_unlock(&_rwlock);
            return false;
        }

        // 获取所有的文件信息-将数据放到数组中
        bool GetAll(std::vector<BackupInfo> *arr)
        {
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.begin();
            for (; it != _table.end(); it++)
            {
                arr->push_back(it->second);
            }
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }

        // 每次数据新增或者修改都要持久化存储，避免数据丢失
        bool Storage()
        {
            // 将所有的数据获取出来，转换成Json格式的字符串存储到文件中

            // 1、获取所有数据
            std::vector<cloud::BackupInfo> array;
            this->GetAll(&array);
            // 2、将所有的数据放到Json::Value类对象中
            Json::Value root;
            for (int i = 0; i < array.size(); i++)
            {
                Json::Value item;
                item["pack_flag"] = array[i].pack_flag;      // 是否压缩标志
                item["real_path"] = array[i].real_path;      // 实际存储路径-根据这个路径获取资源
                item["fsize"] = (Json::Int64)array[i].fsize; // 文件大小
                item["atime"] = (Json::Int64)array[i].atime; // 文件最后一次访问时间
                item["mtime"] = (Json::Int64)array[i].mtime; // 文件最后一次修改时间
                item["pack_path"] = array[i].pack_path;      // 文件压缩包存储路径
                item["url"] = array[i].url;                  // 请求资源路径
                // 添加数组元素使用append，添加到root里面作为整体文件备份信息数组里面的一个元素
                root.append(item);
            }
            // 3、对Json::Value对象数组进行序列化，存放到body中
            std::string body;
            // 调用函数进行序列化
            cloud::JsonUtil::Serialize(root, &body);
            // 4、存储文件-_backup_file是成员变量持久化存储文件s
            cloud::FileUtil fu(_backup_file);
            // 将数据写入文件
            fu.SetContent(body);
            return true;
        }

        // 初始化加载，在每次系统重启都要加载以前的数据
        bool InitLoad()
        {
            FileUtil fu(_backup_file);
            if (fu.Exists() == false)
            {
                return true;
            }
            std::string body;
            fu.GetContent(&body);

            Json::Value root;
            JsonUtil::UnSerialize(body, &root);

            for (int i = 0; i < root.size(); i++)
            {
                BackupInfo info;
                // 注意这时候不能使用NewBackupinfo,因为信息都是从配置文件中拿来的
                info.atime = root[i]["atime"].asInt64();
                info.mtime = root[i]["mtime"].asInt64();
                info.fsize = root[i]["fsize"].asInt64();
                info.pack_flag = root[i]["pack_flag"].asBool();
                info.pack_path = root[i]["pack_path"].asString();
                info.real_path = root[i]["real_path"].asString();
                info.url = root[i]["url"].asString();
                // 调用一个插入接口
                Insert(info);
            }
            return true;
        }
    };
}

#endif